// Copyright (c) Microsoft Corporation.  All rights reserved.

//+----------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// File name:
//      GeometrySink.cpp
//
// Description: 
//      Implements native geometry sink wrapper interfaces that call into a 
//      wrapper implementation of a managed interface supplied by the caller.
//
//-----------------------------------------------------------------------------
#include "stdafx.h"
#include "D2DGeometrySink.h"

namespace Microsoft { namespace WindowsAPICodePack { namespace DirectX { namespace Direct2D1 {


#define STANDARD_PROLOGUE       \
    if (SUCCEEDED(m_hr))        \
    {                           \
        try                     \
        {

#define STANDARD_CATCH_BLOCKS       \
    catch(System::Exception^)     \
    {                               \
        m_hr = E_FAIL;              \
    }                               \


#define STANDARD_EPILOGUE               \
        }                               \
        STANDARD_CATCH_BLOCKS           \
    }                                   \


GeometrySinkCallback::GeometrySinkCallback(
    ISimplifiedGeometrySink ^sink
    ) : m_hr(S_OK),
        m_token(ManagedSinkTable::Register(sink)),
        m_cRef(1)
{
}


STDMETHODIMP
GeometrySinkCallback::QueryInterface(
    __in REFIID riid,
    __out void **ppv
    )
{
    if (ppv == NULL)
    {
        return E_INVALIDARG;
    }

    void *pv = NULL;

    if (riid == __uuidof(IUnknown))
    {
        pv = static_cast<IUnknown *>(this);
    }
    else if (riid == __uuidof(ID2D1SimplifiedGeometrySink))
    {
        pv = static_cast<ID2D1SimplifiedGeometrySink *>(this);
    }
    else if (riid == __uuidof(ID2D1GeometrySink))
    {
        pv = static_cast<ID2D1GeometrySink *>(this);
    }

    if (pv == NULL)
    {
        return E_NOINTERFACE;
    }

    AddRef();

    *ppv = pv;

    return S_OK;
}

STDMETHODIMP_(ULONG)
GeometrySinkCallback::AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG)
GeometrySinkCallback::Release()
{
    LONG cRef = InterlockedDecrement(&m_cRef);

    if (cRef == 0)
    {
        delete this;
    }

    return cRef;
}

STDMETHODIMP_(void)
GeometrySinkCallback::SetFillMode(
    D2D1_FILL_MODE fillMode 
    )
{
    STANDARD_PROLOGUE

    ISimplifiedGeometrySink ^geometrySink = ManagedSinkTable::FindSimplifiedSink(m_token);

    geometrySink->SetFillMode(static_cast<FillMode>(fillMode));

    STANDARD_EPILOGUE
}

STDMETHODIMP_(void)
GeometrySinkCallback::SetSegmentFlags(
    D2D1_PATH_SEGMENT vertexFlags 
    )
{
    STANDARD_PROLOGUE

    ISimplifiedGeometrySink ^geometrySink = ManagedSinkTable::FindSimplifiedSink(m_token);

    geometrySink->SetSegmentFlags(static_cast<PathSegment>(vertexFlags));

    STANDARD_EPILOGUE
}

STDMETHODIMP_(void)
GeometrySinkCallback::BeginFigure(
    __in CONST D2D1_POINT_2F startPoint,
    D2D1_FIGURE_BEGIN figureBegin 
    ) 
{
    STANDARD_PROLOGUE

    ISimplifiedGeometrySink ^geometrySink = ManagedSinkTable::FindSimplifiedSink(m_token);

    Point2F copystartPoint;

    copystartPoint.CopyFrom(startPoint);

    geometrySink->BeginFigure(copystartPoint, static_cast<FigureBegin>(figureBegin));

    STANDARD_EPILOGUE
}

STDMETHODIMP_(void)
GeometrySinkCallback::AddLines(
    __in_ecount(pointsLength) CONST D2D1_POINT_2F *points,
    UINT pointsLength 
    ) 
{
    STANDARD_PROLOGUE

    ISimplifiedGeometrySink ^geometrySink = ManagedSinkTable::FindSimplifiedSink(m_token); 

    cli::array<Point2F> ^arrayCopy = GetArray<Point2F>(points, pointsLength);

    geometrySink->AddLines(arrayCopy);

    STANDARD_EPILOGUE
}

STDMETHODIMP_(void)
GeometrySinkCallback::AddBeziers(
    __in_ecount(aBeziersLength) CONST D2D1_BEZIER_SEGMENT *aBeziers,
    UINT aBeziersLength 
    )
{
    STANDARD_PROLOGUE

    ISimplifiedGeometrySink ^geometrySink = ManagedSinkTable::FindSimplifiedSink(m_token);

    cli::array<BezierSegment> ^arrayCopy = GetArray<BezierSegment>(aBeziers, aBeziersLength);

    geometrySink->AddBeziers(arrayCopy);

    STANDARD_EPILOGUE
}

STDMETHODIMP_(void)
GeometrySinkCallback::EndFigure(
    D2D1_FIGURE_END figureEnd 
    )
{
    STANDARD_PROLOGUE

    ISimplifiedGeometrySink ^geometrySink = ManagedSinkTable::FindSimplifiedSink(m_token);

    geometrySink->EndFigure(static_cast<FigureEnd>(figureEnd));

    STANDARD_EPILOGUE
}

STDMETHODIMP
GeometrySinkCallback::Close()
{
    STANDARD_PROLOGUE

    //
    // We always call Close, even if we are in an error state. The managed interior
    // implementation might need to close stuff and might have an independent error to
    // report.
    // 

    ISimplifiedGeometrySink ^geometrySink = ManagedSinkTable::FindSimplifiedSink(m_token);

    geometrySink->Close();

    STANDARD_EPILOGUE

    return m_hr;
}

STDMETHODIMP_(void)
GeometrySinkCallback::AddLine(
    __in D2D1_POINT_2F vertex 
    )
{
    STANDARD_PROLOGUE

    Point2F point2F;

    point2F.CopyFrom(vertex);

    IGeometrySink ^geometrySink = ManagedSinkTable::FindSink(m_token);

    geometrySink->AddLine(point2F);

    STANDARD_EPILOGUE
}

STDMETHODIMP_(void)
GeometrySinkCallback::AddBezier(
    __in CONST D2D1_BEZIER_SEGMENT *pBezier 
    )
{
    STANDARD_PROLOGUE

    BezierSegment bezierSegment;

    bezierSegment.CopyFrom(*pBezier);

    IGeometrySink ^geometrySink = ManagedSinkTable::FindSink(m_token);

    geometrySink->AddBezier(bezierSegment);

    STANDARD_EPILOGUE
}

STDMETHODIMP_(void)
GeometrySinkCallback::AddQuadraticBezier(
    __in CONST D2D1_QUADRATIC_BEZIER_SEGMENT *pBezier 
    )
{
    STANDARD_PROLOGUE

    QuadraticBezierSegment quadraticBezierSegment;

    quadraticBezierSegment.CopyFrom(*pBezier);

    IGeometrySink ^geometrySink = ManagedSinkTable::FindSink(m_token);

    geometrySink->AddQuadraticBezier(quadraticBezierSegment);

    STANDARD_EPILOGUE
}

STDMETHODIMP_(void)
GeometrySinkCallback::AddQuadraticBeziers(
    __in_ecount(aBeziersLength) CONST D2D1_QUADRATIC_BEZIER_SEGMENT *aBeziers,
    UINT aBeziersLength 
    )
{
    STANDARD_PROLOGUE

    IGeometrySink ^geometrySink = ManagedSinkTable::FindSink(m_token);

    cli::array<QuadraticBezierSegment> ^arrayCopy = GetArray<QuadraticBezierSegment>(aBeziers, aBeziersLength);

    geometrySink->AddQuadraticBeziers(arrayCopy);

    STANDARD_EPILOGUE
}

STDMETHODIMP_(void)
GeometrySinkCallback::AddArc(
    __in CONST D2D1_ARC_SEGMENT *pArc 
    )
{
    STANDARD_PROLOGUE

    ArcSegment arcSegment;

    arcSegment.CopyFrom(*pArc);

    IGeometrySink ^geometrySink = ManagedSinkTable::FindSink(m_token);

    geometrySink->AddArc(arcSegment);

    STANDARD_EPILOGUE
}

GeometrySinkCallback::~GeometrySinkCallback()
{
    ManagedSinkTable::Unregister(m_token);
}

//+----------------------------------------------------------------------------
//
// Managed Sink Table
//
//-----------------------------------------------------------------------------
/*static*/
int
ManagedSinkTable::Register(
    ISimplifiedGeometrySink ^geometrySink
    )
{
    int index = 0;

    //
    // First, try and find a free index.
    // 
    if (sc_geometrySinks != nullptr)
    {
        for(; index < sc_geometrySinks->Length; ++index)
        {
            if (sc_geometrySinks[index] == nullptr)
            {
                break;
            }
        }
    }

    if (sc_geometrySinks == nullptr || sc_geometrySinks->Length <= index)
    {
        cli::array<ISimplifiedGeometrySink ^> ^newArray = gcnew cli::array<ISimplifiedGeometrySink ^>(index + 10);

        if (sc_geometrySinks != nullptr)
        {
            sc_geometrySinks->CopyTo(newArray, 0);
        }

        sc_geometrySinks = newArray;
    }

    sc_geometrySinks[index] = geometrySink;

    return index;
}

/*static*/
ISimplifiedGeometrySink ^
ManagedSinkTable::FindSimplifiedSink(
    int token
    )
{
    return sc_geometrySinks[token];
}

/*static*/
IGeometrySink ^
ManagedSinkTable::FindSink(
    int token
    )
{
    return cli::safe_cast<IGeometrySink ^>(sc_geometrySinks[token]);
}

/*static*/
void
ManagedSinkTable::Unregister(
    int token
    )
{
    //
    // We don't dispose since we assume that the contract is to call Close.
    // 
    sc_geometrySinks[token] = nullptr;
}


} } } }
